using System;
using System.Collections;
using System.Collections.Generic;

namespace SBPweb.Forge.Modules.Modularity
{
	/// <summary>
	/// TypeDictionary. The name of the object have to be unique.
	/// </summary>
    public abstract class TypeDictionary : IEnumerable
	{

		#region Protected field

		/// <summary>
		/// Dictionary&lt;Type, object&gt; pTypeDictionary
		/// </summary>
		protected Dictionary<Type, object> typeDictionary;
		/// <summary>
		/// Dictionary&lt;string, object&gt; pNameDictionary
		/// </summary>
        protected Dictionary<string, object> nameDictionary;
		/// <summary>
		/// Dictionary&lt;Type, List&lt;EventHandler&lt;TypeDictionaryItemEventArgs&gt;&gt;&gt; pDOnAdded
		/// </summary>
        protected Dictionary<Type, List<EventHandler<TypeDictionaryItemEventArgs>>> dictOnAdded;
		/// <summary>
		/// Dictionary&lt;Type, List&lt;EventHandler&lt;TypeDictionaryItemEventArgs&gt;&gt;&gt; pDOnChanged
		/// </summary>
        protected Dictionary<Type, List<EventHandler<TypeDictionaryItemEventArgs>>> dictOnChanged;
		/// <summary>
		/// Dictionary&lt;Type, List&lt;EventHandler&lt;TypeDictionaryItemEventArgs&gt;&gt;&gt; pDOnRemoved
		/// </summary>
        protected Dictionary<Type, List<EventHandler<TypeDictionaryItemEventArgs>>> dictOnRemoved;

		#endregion

		#region Constructor logic

		/// <summary>
		/// initialize a new TypeDictionary instance
		/// </summary>
		public TypeDictionary()
		{
			typeDictionary = new Dictionary<Type, object>();
            nameDictionary = new Dictionary<string, object>();

            dictOnAdded = new Dictionary<Type, List<EventHandler<TypeDictionaryItemEventArgs>>>();
            dictOnChanged = new Dictionary<Type, List<EventHandler<TypeDictionaryItemEventArgs>>>();
            dictOnRemoved = new Dictionary<Type, List<EventHandler<TypeDictionaryItemEventArgs>>>();
        }

		#endregion

		#region Property accessors

		/// <summary>
		/// overload of array access
		/// </summary>
		/// <param name="key">Type of object</param>
		/// <returns></returns>
		public object this[Type key]
		{
			get
			{
				return typeDictionary[key];
			}
		}

        /// <summary>
        /// overload of array access
        /// </summary>
        /// <param name="key">Key of the object</param>
        /// <returns></returns>
		public object this[string key]
        {
            get
            {
                return nameDictionary[key];
            }
        }

		#endregion

        #region Methods

		/// <summary>
		/// add new object to TypeDictionary
		/// </summary>
		/// <param name="name">Name of object</param>
        /// <param name="type">Type of service</param>
		/// <param name="value">object Instance</param>
        public virtual void Add(string name, Type type, object value)
        {
            nameDictionary.Add(name, value);
            try
            {
                typeDictionary.Add(type, value);
            }
            catch
            {
                //Overflow
            }
            FireOnAdded(name, type, value);
        }

		/// <summary>
		/// Remove Service from TypeDictionary
		/// </summary>
		/// <param name="key">Type of Service</param>
		/// <returns>if success</returns>
        public virtual bool Remove(Type key)
        {
            try
            {
                object service = typeDictionary[key];
                string skey = string.Empty;
                typeDictionary.Remove(key);
                foreach (KeyValuePair<string, object> kvp in nameDictionary)
                {
                    if (kvp.Value == service) skey = kvp.Key;
                }
                nameDictionary.Remove(skey);

                FireOnRemoved(skey, key, service);

                return true;
            }
            catch
            {
                return false;
            }
        }

		/// <summary>
		/// Remove Service from TypeDictionary
		/// </summary>
		/// <param name="name">Name of Service</param>
		/// <returns>if success</returns>
        public virtual bool Remove(string name)
        {
            try
            {
                object o = nameDictionary[name];
                nameDictionary.Remove(name);
                Type tkey = null;
                foreach (KeyValuePair<Type, object> kvp in typeDictionary)
                {
                    if (kvp.Value == o) tkey = kvp.Key;
                }
                typeDictionary.Remove(tkey);

                FireOnRemoved(name, tkey, o);
                return true;
            }
            catch
            {
                return false;
            }
        }

		/// <summary>
		/// ContainsKey overload
		/// </summary>
		/// <param name="key">Type of object</param>
		/// <returns>true if TypeDictionary contains the object</returns>
		public bool ContainsKey(Type key)
		{
            return typeDictionary.ContainsKey(key);
		}

		/// <summary>
		/// ContainsKey overload
		/// </summary>
		/// <param name="key">Key of the object</param>
		/// <returns>true if TypeDictionary contains the object</returns>
        public bool ContainsKey(string key)
        {
            return nameDictionary.ContainsKey(key);
        }

        /// <summary>
        /// Generic access of items
        /// </summary>
        /// <typeparam name="T">Type of item</typeparam>
        /// <returns></returns>
		public T Get<T>()
		{
            Type key = typeof(T);
			if (ContainsKey(key))
			{
				return (T)this[key];
			}
			else
			{
				return default(T);
			}
		}

		/// <summary>
		/// Generic access of items
		/// </summary>
		/// <typeparam name="T">Type of item</typeparam>
		/// <param name="name">Name of item</param>
		/// <returns></returns>
		public T Get<T>(string name)
		{
			if (ContainsKey(name))
			{
				return (T)this[name];
			}
			else
			{
				return default(T);
			}
		}

        /// <summary>
        /// Generic write of items
        /// </summary>
        /// <typeparam name="T">Type of item</typeparam>
        /// <param name="value">The item value</param>
        public bool Put<T>(T value)
        {
            return Put<T>(typeof(T).FullName, value);
        }

        /// <summary>
        /// Generic write of items
        /// </summary>
        /// <typeparam name="T">Type of item</typeparam>
        /// <param name="name">Name of item</param>
        /// <param name="value">The item value</param>
        /// <returns>True when overwrite</returns>
        public virtual bool Put<T>(string name, T value)
        {
            if (nameDictionary.ContainsKey(name))
            {
                object oldValue = nameDictionary[name];
                nameDictionary[name] = value;

                Type type = null;

                foreach (KeyValuePair<Type, object> kvp in typeDictionary)
                {
                    if (kvp.Value == oldValue && kvp.Key == typeof(T))
                    {
                        type = kvp.Key;
                        break;
                    }
                }

                if (type != null)
                    typeDictionary[type] = value;

                FireOnChanged(name, typeof(T), value);

                return true;
            }
            else
            {
                Add(name, typeof(T), value);
                return false;
            }
        }

		#endregion

		#region Public event handler handler methods

		/// <summary>
		/// Adds an OnAddedHandler for type T
		/// </summary>
		/// <typeparam name="T">type of the item</typeparam>
		/// <param name="handler">EventHandler&lt;TypeDictionaryItemEventArgs&gt;</param>
		public void AddOnAddedHandler<T>(EventHandler<TypeDictionaryItemEventArgs> handler)
        {
            if (!dictOnAdded.ContainsKey(typeof(T)))
            {
                dictOnAdded.Add(typeof(T), new List<EventHandler<TypeDictionaryItemEventArgs>>());
            }
            dictOnAdded[typeof(T)].Add(handler);
        }

		/// <summary>
		/// Add an OnChangedHandler for type T
		/// </summary>
		/// <typeparam name="T">type of the item</typeparam>
		/// <param name="handler">EventHandler&lt;TypeDictionaryItemEventArgs&gt;</param>
		public void AddOnChangedHandler<T>(EventHandler<TypeDictionaryItemEventArgs> handler)
        {
            if (!dictOnChanged.ContainsKey(typeof(T)))
            {
                dictOnChanged.Add(typeof(T), new List<EventHandler<TypeDictionaryItemEventArgs>>());
            }
            dictOnChanged[typeof(T)].Add(handler);
        }

		/// <summary>
		/// Adds an OnRemovedHandler for type T
		/// </summary>
		/// <typeparam name="T">type of the item</typeparam>
		/// <param name="handler">EventHandler&lt;TypeDictionaryItemEventArgs&gt;</param>
		public void AddOnRemovedHandler<T>(EventHandler<TypeDictionaryItemEventArgs> handler)
        {
            if (!dictOnRemoved.ContainsKey(typeof(T)))
            {
                dictOnRemoved.Add(typeof(T), new List<EventHandler<TypeDictionaryItemEventArgs>>());
            }
            dictOnRemoved[typeof(T)].Add(handler);
        }

		/// <summary>
		/// Removes an OnAddedHandler for type T
		/// </summary>
		/// <typeparam name="T">type of the item</typeparam>
		/// <param name="handler">EventHandler&lt;TypeDictionaryItemEventArgs&gt;</param>
		public void RemoveOnAddedHandler<T>(EventHandler<TypeDictionaryItemEventArgs> handler)
        {
            if (dictOnAdded.ContainsKey(typeof(T)))
            {
                dictOnAdded[typeof(T)].Remove(handler);
            }
        }

		/// <summary>
		/// Removes an OnChangedHandler for type T
		/// </summary>
		/// <typeparam name="T">type of the item</typeparam>
		/// <param name="handler">EventHandler&lt;TypeDictionaryItemEventArgs&gt;</param>
		public void RemoveOnChangedHandler<T>(EventHandler<TypeDictionaryItemEventArgs> handler)
        {
            if (dictOnChanged.ContainsKey(typeof(T)))
            {
                dictOnChanged[typeof(T)].Remove(handler);
            }
        }

        /// <summary>
        /// Removes an OnRemovedHandler for type T
        /// </summary>
		/// <typeparam name="T">type of the item</typeparam>
		/// <param name="handler">EventHandler&lt;TypeDictionaryItemEventArgs&gt;</param>
		public void RemoveOnRemovedHandler<T>(EventHandler<TypeDictionaryItemEventArgs> handler)
        {
            if (dictOnRemoved.ContainsKey(typeof(T)))
            {
                dictOnRemoved[typeof(T)].Remove(handler);
            }
		}

		#endregion

		#region Protected event fire methods

		/// <summary>
		/// Fires an OnAdded event
		/// </summary>
		/// <param name="name">name of the item</param>
		/// <param name="type">type of the item</param>
		/// <param name="value">value of the item</param>
		protected void FireOnAdded(string name, Type type, object value)
		{
			if (dictOnAdded.ContainsKey(type))
			{
				foreach (EventHandler<TypeDictionaryItemEventArgs> handler in dictOnAdded[type])
				{
					handler.DynamicInvoke(this, new TypeDictionaryItemEventArgs(name, type, value));
				}
			}
		}

		/// <summary>
		/// Fires an OnChanged event
		/// </summary>
		/// <param name="name">name of the item</param>
		/// <param name="type">type of the item</param>
		/// <param name="value">value of the item</param>
		protected void FireOnChanged(string name, Type type, object value)
		{
			if (dictOnChanged.ContainsKey(type))
			{
				foreach (EventHandler<TypeDictionaryItemEventArgs> handler in dictOnChanged[type])
				{
					handler.DynamicInvoke(this, new TypeDictionaryItemEventArgs(name, type, value));
				}
			}
		}

		/// <summary>
		/// Fires an OnRemoved event
		/// </summary>
		/// <param name="name">name of the item</param>
		/// <param name="type">type of the item</param>
		/// <param name="value">value of the item</param>
		protected void FireOnRemoved(string name, Type type, object value)
		{
			if (dictOnRemoved.ContainsKey(type))
			{
				foreach (EventHandler<TypeDictionaryItemEventArgs> handler in dictOnRemoved[type])
				{
					handler.DynamicInvoke(this, new TypeDictionaryItemEventArgs(name, type, value));
				}
			}
		}

		#endregion

        #region IEnumerable Members

		/// <summary>
		/// Returns the Enumerator for TypeDictionary
		/// </summary>
		/// <returns>the IEnumerator insance of NameDictionary</returns>
        public IEnumerator GetEnumerator()
        {
            return nameDictionary.GetEnumerator();
        }

        #endregion

    }

	/// <summary>
	/// A class for TypeDictionary events
	/// </summary>
    public class TypeDictionaryItemEventArgs : EventArgs
	{

		#region Private members

		private string name;
        private Type type;
        private object value;

		#endregion

		#region Constructor logic

		/// <summary>
		/// Initializes a new TypeDictionaryItemEventArgs instance
		/// </summary>
		/// <param name="name">string name</param>
		/// <param name="type">Type type</param>
		/// <param name="value">object value</param>
		public TypeDictionaryItemEventArgs(string name, Type type, object value)
        {
            this.name = name;
            this.type = type;
            this.value = value;
		}

		#endregion

		#region Public properties

		/// <summary>
		/// Returns the Name of the item
		/// </summary>
		public string Name
        {
            get
            {
                return name;
            }
            internal set
            {
                name = value;
            }
        }

		/// <summary>
		/// Retuns the Type of the item
		/// </summary>
		public Type Type
        {
            get
            {
                return type;
            }
            internal set
            { 
                type = value; 
            }
        }

		/// <summary>
		/// Returns the Value of the item
		/// </summary>
		public object Value
        {
            get
            {
                return value;
            }
            internal set
            {
                value = value;
            }
		}

		#endregion

	}
}